package sqldbManagement.dbparklot.cj.aop;

import sqldbManagement.dbparklot.cj.anno.Delete;
import sqldbManagement.dbparklot.cj.anno.Insert;
import sqldbManagement.dbparklot.cj.anno.Select;
import sqldbManagement.dbparklot.cj.anno.Update;
import sqldbManagement.dbparklot.cj.utils.JDBCUtil;

import java.lang.reflect.*;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

public class Mapper {

    static int index;

    public static <T> T getMapper(Class<T> clazz) {

        T o = (T)Proxy.newProxyInstance(clazz.getClassLoader(),new Class[]{clazz}, (obj, method, args) -> {
            index = 0;
            // 开启链接
            Connection conn = JDBCUtil.getConnection();
            // 获取语句对象并处理
            PreparedStatement state = getPreparedStatement(conn, method);
            initPreparedStatement(args, state);

            if(index == 1){
                int i = state.executeUpdate();
                JDBCUtil.close(conn,state);
                if(method.getReturnType().toString().equals("void"))
                    return null;
                return i;
            }else if(index == 4){
                List<?> list = submit(state, method.getReturnType(), method.getAnnotation(Select.class).type());
                if(method.getReturnType().toString().contains("List"))
                    return list;
                if(list != null && list.size() > 0)
                    return list.get(0);
            }
            return null;
        });
        return o;
    }

    private static <T> List<T> submit(PreparedStatement state, Class tClass, Class<T> aClass) {
        assert !aClass.toString().contains("List");
        try {
            List<T> list = new ArrayList<>();

            ResultSet rs = state.executeQuery();
            ResultSetMetaData data = rs.getMetaData();

            while (rs.next()){
                T t = aClass.getDeclaredConstructor().newInstance();
                for(int i = 0 ; i < data.getColumnCount(); i++){
                    String columnLabel = data.getColumnLabel(i + 1);
                    Object object = rs.getObject(columnLabel);

                    if(t instanceof Map){
                        ((Map<String, Object>) t).put(columnLabel,object);
                        continue;
                    }

                    Field field = null;
                    try{
                        field = t.getClass().getDeclaredField(columnLabel);
                    }catch (Exception e){
                        continue;
                    }
                    field.setAccessible(true);
                    field.set(t,object);
                    field.setAccessible(false);
                }
                list.add(t);
            }
            return list;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    private static void initPreparedStatement(Object[] args,PreparedStatement state){
        try{
            int i = 0;
            for (Object arg : args) {
                Class<?> aClass = arg.getClass();
                String[] split = aClass.getName().split("\\.");
                String type = split[split.length - 1];

                if(type.equals("Integer")){
                    aClass = int.class;
                    type = "Int";
                }else if(type.equals("Boolean"))
                    aClass = boolean.class;
                else if(type.equals("Double"))
                    aClass = double.class;
                else if(type.equals("Float"))
                    aClass = float.class;
                else if(type.equals("Short"))
                    aClass = short.class;
                else if(type.equals("Long"))
                    aClass = long.class;
                else if(type.equals("Boolean"))
                    aClass = boolean.class;
                else if(type.equals("Byte"))
                    aClass = byte.class;

                type = "set"+type;

                Method m = state.getClass().getMethod(type, int.class, aClass);
                m.invoke(state,++i,arg);
            }

        }catch (Exception e){
        }

    }

    private static PreparedStatement getPreparedStatement(Connection conn,Method method){
        Update update = method.getAnnotation(Update.class);
        Insert insert = method.getAnnotation(Insert.class);
        Delete delete = method.getAnnotation(Delete.class);
        Select select = method.getAnnotation(Select.class);

        try{
            if(update != null){
                index = 1;
                return conn.prepareStatement(update.value());
            }else if(insert != null){
                index = 1;
                return conn.prepareStatement(insert.value());
            }else if(delete != null){
                index = 1;
                return conn.prepareStatement(delete.value());
            }else if(select != null){
                index = 4;
                return conn.prepareStatement(select.value());
            }else index = 0;

        }catch (Exception e){
            return null;
        }
        return null;
    }

}
